/**
 * @function 表单验证
 * @encode utf-8
 * @copyright (C) lijun
 * @file validator.js
 * @author lijun
 * @version 1.1
 * @date 2010-3-21
 */

/**
 * 验证构造函数
 */
XLib.V = {
    listener : {},
    checkList : null,
    result : {},
    Types : {
        VOID        : -1,       //不校验
        INT         : 1,        //整型
        LONG        : 2,        //长整型        @!!!
        FLOAT       : 4,        //浮点型        @!!!
        DOUBLE      : 8,        //双精度浮点型  @!!!
        BYTE        : 16,       //字节
        SHORT       : 32,       //short类型
        NUMERIC     : 64,       //数值
        BOOLEAN     : 128,      //布尔型
        CURRENCY    : 512,      //金额
        STRING      : 1024,     //字符串
        FILTER      : 2048,     //过滤特殊字符的字符串
        CHAR        : 4096,     //字符
        EN          : 8192,     //英文字符/字符串
        CN          : 16384,    //中文字符/字符串
        LETTER      : 32768,    //单个字母
        EN_NUM      : 65536,    //英文和数字组合
        TEL         : 131072,   //电话号码
        MOBILE      : 262144,   //手机号码
        URL         : 524288,   //URL
        EMAIL       : 1048576,  //EMAIL
        CNID        : 2097152,  //大陆身份证号码
        HKID        : 4194304,  //香港身份证号码
        QQ          : 8388608,  //QQ号码
        DATETIME    : 16777216, //日期
        UINT        : 33554432, //无符号整数
        DWORD       : 67108864  //双字节字符
    },
    check : {
        /**
         * 是否为空
         * @param v 需要校验的值
         * @return true/false
         */
        isEmpty : function(v)
        {
            return ("" == v.trim());
        },
        /**
         * 是否未定义
         * @param v 需要校验的值
         * @return true/false
         */
        isUndefined : function(v)
        {
            return (undefined === v);
        },
        /**
         * 是否为null
         * @param v 需要校验的值
         * @return true/false
         */
        isNull : function(v)
        {
            return (null === v);
        },
        /**
         * 是否为int
         * @param v 需要校验的值
         * @return true/false
         */
        isInt : function(v)
        {
            var p = /^(([\+\-]?\d+)|(\d(\.\d+)?[e|E][\+\-]?\d+))$/mi;
            var t = p.test(v);
            var i = 0;
            if(!t)
            {
                return false;
            }
            else
            {
                i = v * 1;
                if(i < Math.pow(-2, 31) || i > Math.pow(2, 31) - 1)
                {
                    return false;
                }
            }
            return true;
        },
        /**
         * 是否为uint
         * @param v 需要校验的值
         * @return true/false
         */
        isUint : function(v)
        {
            var p = /^(([\+]?\d+)|(\d(\.\d+)?[e|E][\+\-]?\d+))$/mi;
            var t = p.test(v);
            var i = 0;
            if(!t)
            {
                return false;
            }
            else
            {
                i = v * 1;
                if(i > (Math.pow(2, 32) - 1) || i < 0)
                {
                    return false;
                }
            }
            return true;
        },
        /**
         * 是否为long
         * @param v 需要校验的值
         * @return true/false
         */
        isLong : function(v)
        {
            var p = /^(([\+\-]?\d+)|(\d(\.\d+)?[e|E][\+\-]?\d+))$/mi;
            var t = p.test(v);
            var i = 0;
            if(!t)
            {
                return false;
            }
            else
            {
                i = v * 1;
                if(i < -Math.pow(2, 63)&& i > Math.pow(2, 63) - 1)
                {
                    return false;
                }
            }
            return true;
        },
        /**
         * 是否为float
         * @param v 需要校验的值
         * @return true/false
         */
        isFloat : function(v)
        {
            var p = /^(([\+\-]?\d+(\.\d+)?)|(\d(\.\d+)?[e|E][\+\-]?\d+))$/mi;
            var t = p.test(v);
            var i = 0;
            if(!t)
            {
                return false;
            }
            else
            {
                i = v * 1;
                if(i < 3.4E-45 && i > 1.4E+38)
                {
                    return false;
                }
            }
            return true;
        },
        /**
         * 是否为double
         * @param v 需要校验的值
         * @return true/false
         */
        isDouble : function(v)
        {
            var p = /^(([\+\-]?\d+(\.\d+))|(\d(\.\d+)?[e|E][\+\-]?\d+))?$/mi;
            var t = p.test(v);
            var i = 0;
            if(!t)
            {
                return false;
            }
            else
            {
                i = v * 1;
                if(i < 4.9E-324 && i > 1.8E+308)
                {
                    return false;
                }
            }
            return true;
        },
        /**
         * 是否为byte
         * @param v 需要校验的值
         * @return true/false
         */
        isByte : function(v)
        {
            var p = /^[\+\-]?\d+$/mi;
            var t = p.test(v);
            var i = 0;
            if(!t)
            {
                return false;
            }
            else
            {
                i = v * 1;
                if(i < -128 && i > 127)
                {
                    return false;
                }
            }
            return true;
        },
        /**
         * 是否为short
         * @param v 需要校验的值
         * @return true/false
         */
        isShort : function(v)
        {
            var p = /^[\+\-]?\d+$/mi;
            var t = p.test(v);
            var i = 0;
            if(!t)
            {
                return false;
            }
            else
            {
                i = v * 1;
                if(i < -32768 && i > 32767)
                {
                    return false;
                }
            }
            return true;
        },
        /**
         * 是否为numeric
         * @param v 需要校验的值
         * @return true/false
         */
        isNumeric : function(v)
        {
            var p = /^[\+\-]?\d+(\.\d+)?$/mi;
            var t = p.test(v);
            return t;
        },
        /**
         * 是否为boolean
         * @param v 需要校验的值
         * @return true/false
         */
        isBoolean : function(v)
        {
            if(typeof(v) === "boolean")
            {
                return true;
            }
            else if(typeof(v) === "string")
            {
                var p = /^(true|false|0|1)$/mi;
                return p.test(v);
            }
            else if(typeof(v) === "number")
            {
                var p = /^(0|1)$/mi;
                return p.test(v);
            }
            else
            {
                return false;
            }
        },
        /**
         * 是否为currency
         * @param v 需要校验的值
         * @return true/false
         */
        isCurrency : function(v)
        {
            var p = /^(([\+\-]?\d+(\.\d+)?)|([\+\-]?[1-9]{1,3}(\,\d{3})+(\.\d+)?))$/mi;
            return p.test(v);
        },
        /**
         * 是否为string
         * @param v 需要校验的值
         * @return true/false
         */
        isString : function(v)
        {
            return (typeof(v) == "string" && !this.isEmpty(v));
        },
        /**
         * 是否为filter
         * @param v 需要校验的值
         * @return true/false
         */
        isFilter : function(v)
        {
            var p = /^[^\\\/\?\:\"\<\>\|]+$/mi;
            return (p.test(v) && !this.isEmpty(v));
        },
        /**
         * 是否为char
         * @param v 需要校验的值
         * @return true/false
         */
        isChar : function(v)
        {
            var p = /^[\u0000-\uFFFF]$/mi;
            return (p.test(v) && !this.isEmpty(v));
        },
        /**
         * 是否为en
         * @param v 需要校验的值
         * @return true/false
         */
        isEN : function(v)
        {
            var p = /^[a-z]+$/mi;
            return p.test(v);
        },
        /**
         * 是否为cn
         * @param v 需要校验的值
         * @return true/false
         */
        isCN : function(v)
        {
            var p = /^[\u4e00-\u9fa5]+$/mi;
            return p.test(v);
        },
        /**
         * 是否为双字节字符
         * @param v 需要校验的值
         * @return true/false
         */
        isDWord : function(v)
        {
            var p = /^[^\x00-\xff]+$/mi;
            return p.test(v);
        },
        /**
         * 是否为letter
         * @param v 需要校验的值
         * @return true/false
         */
        isLetter : function(v)
        {
            var p = /^[a-z]$/mi;
            return p.test(v);
        },
        /**
         * 是否为en_num
         * @param v 需要校验的值
         * @return true/false
         */
        isEN_NUM : function(v)
        {
            var p = /^[a-z0-9]+$/mi;
            return p.test(v);
        },
        /**
         * 是否为tel
         * @param v 需要校验的值
         * @return true/false
         */
        isTEL : function(v)
        {
            var p = /^(\+?\d{1,4})?([\- ]?\d{3,5})?([\- ]?\d{7,8})([\- ]?\d{3,5})?$/mi;
            return p.test(v);
        },
        /**
         * 是否为mobile
         * @param v 需要校验的值
         * @return true/false
         */
        isMobile : function(v)
        {
            var p = /^1[358]\d{9}$/mi;
            return p.test(v);
        },
        /**
         * 是否为url
         * @param v 需要校验的值
         * @return true/false
         */
        isURL : function(v)
        {
            var p = /^(http|https|ftp|file)\:\/\/[\w\W]+$/mi;
            return p.test(v);
        },
        /**
         * 是否为email
         * @param v 需要校验的值
         * @return true/false
         */
        isEmail : function(v)
        {
            var p = /^[^\s\@]+\@[^\s\@]+$/mi;
            return p.test(v);
        },
        /**
         * 是否为cnid
         * @param v 需要校验的值
         * @return true/false
         */
        isCNID : function(v)
        {
            var wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1];
            var vi = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2];
            var ai = [];
            var tmp = null;
            if(v.length != 15 && v.length != 18)
            {
                return false;
            }
            function getVerify(id) //获取末尾标识位
            {
                var remain = 0;
                var sum = 0;
                var k = 0;
                if(id.length == 18)
                {
                    id = id.substring(0, 17);
                }
                for(var i = 0; i < 17; i++)
                {
                    k = id.substring(i, i + 1);
                    ai[i] = k * 1;
                }
                for(var j = 0; j < 17; j++)
                {
                    sum += wi[j] * ai[j];
                }
                remain = sum % 11;
                return vi[remain];
            }
            if(v.length == 15) //将15身份证升级到18位再校检
            {
                tmp = v.substring(0, 6);
                tmp = tmp + "19";
                tmp = tmp + v.substring(6, 15);
                tmp = tmp + getVerify(tmp);
                v = tmp;
            }
            return (getVerify(v) == v.substring(17, 18));
        },
        /**
         * 是否为cnid
         * @param v 需要校验的值
         * @return true/false
         */
        isHKID : function(v)
        {
            var wi = {'A':1, 'B':2, 'C':3, 'D':4, 'E':5, 'F':6, 'G':7, 'H':8, 'I':9, 'J':10, 'K':11, 'L':12, 'M':13, 'N':14, 'O':15, 'P':16, 'Q':17, 'R':18, 'S':19, 'T':20, 'U':21, 'V':22, 'W':23, 'X':24, 'Y':25, 'Z':26};
            var tmp = v.substring(0, 7);
            var a = tmp.split("");
            var t = null;
            var sum = 0;
            var verify = 0;
            var vi = v.substring(8, 9) * 1;
            for(var i = 0, j = 8; i < 7; i++, j--)
            {
                t = wi[a[i]] || a[i];
                sum += t * j;
            }
            verify = sum % 11 == 0 ? 0 : 11 - sum % 11;
            return (vi == verify);
        },
        /**
         * 是否为QQ
         * @param v 需要校验的值
         * @return true/false
         */
        isQQ : function(v)
        {
            var p = /^[1-9]\d{5, 10}$/mi;
            return p.test(v);
        },
        /**
         * 是否为日期
         * @param v 需要校验的值
         * @return true/false
         */
        isDatetime : function(v)
        {
            var fmts = [
                "%y%M%d", "%y-%M-%d", "%y.%M.%d", "%y年%M月%d日", "%y年%M月%d",
                "%M%d%y", "%M-%d-%y", "%M.%d.%y", "%M月%d日%y年", "%M月%d日%y",
                "%d%M%y", "%d-%M-%y", "%d.%M.%y", "%d日%M月%y年", "%d日%M月%y",
                "%M/%d, %w", "%d/%M, %w", "%M/%d, %W", "%d/%M, %W",
                "%M/%d, %a", "%d/%M, %a", "%M/%d, %A", "%d/%M, %A",
                "%y%M%d %h:%m:%s", "%y-%M-%d %h:%m:%s", "%y.%M.%d %h:%m:%s", "%y年%M月%d日 %h:%m:%s", "%y年%M月%d %h:%m:%s",
                "%M%d%y %h:%m:%s", "%M-%d-%y %h:%m:%s", "%M.%d.%y %h:%m:%s", "%M月%d日%y年 %h:%m:%s", "%M月%d日%y %h:%m:%s",
                "%d%M%y %h:%m:%s", "%d-%M-%y %h:%m:%s", "%d.%M.%y %h:%m:%s", "%d日%M月%y年 %h:%m:%s", "%d日%M月%y %h:%m:%s",
                "%M/%d %h:%m:%s, %w", "%d/%M %h:%m:%s, %w", "%M/%d %h:%m:%s, %W", "%d/%M %h:%m:%s, %W",
                "%M/%d %h:%m:%s, %a", "%d/%M %h:%m:%s, %a", "%M/%d %h:%m:%s, %A", "%d/%M %h:%m:%s, %A",
                "%y%M%d %h:%m", "%y-%M-%d %h:%m", "%y.%M.%d %h:%m", "%y年%M月%d日 %h:%m", "%y年%M月%d %h:%m",
                "%M%d%y %h:%m", "%M-%d-%y %h:%m", "%M.%d.%y %h:%m", "%M月%d日%y年 %h:%m", "%M月%d日%y %h:%m",
                "%d%M%y %h:%m", "%d-%M-%y %h:%m", "%d.%M.%y %h:%m", "%d日%M月%y年 %h:%m", "%d日%M月%y %h:%m",
                "%M/%d %h:%m, %w", "%d/%M %h:%m, %w", "%M/%d %h:%m, %W", "%d/%M %h:%m, %W",
                "%M/%d %h:%m, %a", "%d/%M %h:%m, %a", "%M/%d %h:%m, %A", "%d/%M %h:%m, %A",
                "%y%M%d %h", "%y-%M-%d %h", "%y.%M.%d %h", "%y年%M月%d日 %h", "%y年%M月%d %h",
                "%M%d%y %h", "%M-%d-%y %h", "%M.%d.%y %h", "%M月%d日%y年 %h", "%M月%d日%y %h",
                "%d%M%y %h", "%d-%M-%y %h", "%d.%M.%y %h", "%d日%M月%y年 %h", "%d日%M月%y %h",
                "%M/%d %h, %w", "%d/%M %h, %w", "%M/%d %h, %W", "%d/%M %h, %W",
                "%M/%d %h, %a", "%d/%M %h, %a", "%M/%d %h, %A", "%d/%M %h, %A"
            ];
            var size = fmts.length;
            var valid = null;
            var date = new Date();
            for(var i = 0; i < size; i++)
            {
                valid = date.validDate(v, fmts[i]);
                if(valid.isValid)
                {
                    return true;
                }
            }
            return false;
        }
    },
    /**
     * 是否匹配
     * @param type 类型
     * @param value 值
     * @return true/false
     */
    isMatch : function(type, value)
    {
        var _empty = this.check.isEmpty(value);
        var _null = this.check.isNull(value);
        var _undefined = this.check.isUndefined(value);
        switch(type)
        {
            case this.Types.VOID        : //不校验
                return {empty:_empty, nul:_null, undefine:_undefined, match:true};
            case this.Types.INT         : //整型
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isInt(value)};
            case this.Types.UINT        : //无符号整型
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isUint(value)};
            case this.Types.LONG        : //长整型
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isLong(value)};
            case this.Types.FLOAT       : //浮点型
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isFloat(value)};
            case this.Types.DOUBLE      : //双精度浮点型
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isDouble(value)};
            case this.Types.BYTE        : //字节
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isByte(value)};
            case this.Types.SHORT       : //short类型
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isShort(value)};
            case this.Types.NUMERIC     : //数值
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isNumeric(value)};
            case this.Types.BOOLEAN     : //布尔型
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isBoolean(value)};
            case this.Types.CURRENCY    : //金额
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isCurrency(value)};
            case this.Types.STRING      : //字符串
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isString(value)};
            case this.Types.FILTER      : //过滤特殊字符的字符串
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isFilter(value)};
            case this.Types.CHAR        : //字符
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isChar(value)};
            case this.Types.EN          : //英文字符/字符串
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isEN(value)};
            case this.Types.CN          : //中文字符/字符串
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isCN(value)};
            case this.Types.DWORD       : //双字节字符
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isDWord(value)};
            case this.Types.LETTER      : //单个字母
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isLetter(value)};
            case this.Types.EN_NUM      : //英文和数字组合
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isEN_NUM(value)};
            case this.Types.TEL         : //电话号码
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isTEL(value)};
            case this.Types.MOBILE      : //手机号码
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isMobile(value)};
            case this.Types.URL         : //URL
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isURL(value)};
            case this.Types.EMAIL       : //EMAIL
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isEmail(value)};
            case this.Types.CNID        : //大陆身份证号码
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isCNID(value)};
            case this.Types.HKID        : //香港身份证号码
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isHKID(value)};
            case this.Types.QQ          : //QQ号码
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isQQ(value)};
            case this.Types.DATETIME    : //日期
                return {empty:_empty, nul:_null, undefine:_undefined, match:this.check.isDatetime(value)};
            default:
                throw new Error("unknown data type!");
        }
    },
    /**
     * 设置校验结果
     */
    setResult : function(name, value, key)
    {
        key = (key||"form0");
        this.result[key] = this.result[key] || {};
        this.result[key][name] = value;
    },
    /**
     * 获取校验结果
     */
    getResult : function(name, key)
    {
        key = (key||"form0");
        this.result[key] = this.result[key] || {};
        return this.result[key][name];
    },
    /**
     * 验证
     */
    validate : function(key)
    {
        var list = this.checkList;
        var size = list.length;
        var item = null;
        var node = null;
        var strEvents = null;
        var events = null;
        var eSize = 0;

        for(var i = 0; i < size; i++)
        {
            item = list[i];
            strEvents = item.events || "";
            events = strEvents ? strEvents.split("|") : [];
            eSize = events.length;
            node = XLib.DOM.$E(item.id);
            if(null != node && eSize > 0)
            {
                XLib.Event.dispatchEvent(node, events[0], null, null);
            }
        }

        key = (key||"form0");
        this.result[key] = this.result[key] || {};
        for(var name in this.result[key])
        {
            if(this.result[(key||"form0")].hasOwnProperty(name))
            {
                if(false === this.result[(key||"form0")][name])
                {
                    return false;
                }
            }
        }
        return true;
    },
    /**
     * 绑定表单元素
     * @param list [{id, type, events, handler, args}]
     */
    bind : function(list)
    {
        var items = list || [];
        var size = items.length;
        var node = null;
        var item = null;
        var strEvents = null;
        var events = null;
        var eSize = 0;
        var o = null;
        this.checkList = items;
        for(var i = 0; i < size; i++)
        {
            item = items[i];
            strEvents = item.events || "";
            events = strEvents ? strEvents.split("|") : [];
            eSize = events.length;
            node = XLib.DOM.$E(item.id);
            if(null != node)
            {
                for(var j = 0; j < eSize; j++)
                {
                    o = {"handler":function(v, type, handler, args){
                            args.unshift(v.isMatch(type, this.value));
                            handler.apply(this, args);
                            args.shift();
                        }};
                    this.listener[item.id + "_" + events[j]] = o;
                    XLib.Event.addEventListener(node, events[j], o.handler, [this, item.type, item.handler, item.args], false);
                }
            }
        }
    },
    /**
     * 取消校验绑定
     * @param list [{id, type, events, handler, args}]
     */
    unbind : function(list)
    {
        var items = list || [];
        var size = items.length;
        var node = null;
        var item = null;
        var strEvents = null;
        var events = null;
        var eSize = 0;
        for(var i = 0; i < size; i++)
        {
            item = items[i];
            strEvents = item.events || "";
            events = strEvents ? strEvents.split("|") : [];
            eSize = events.length;
            node = XLib.DOM.$E(item.id);
            if(null != node)
            {
                for(var j = 0; j < eSize; j++)
                {
                    if(this.listener[item.id + "_" + events[j]])
                    {
                        XLib.Event.removeEventListener(node, events[j], this.listener[item.id + "_" + events[j]]["handler"], [this, item.type, item.handler, item.args], false);
                        delete this.listener[item.id + "_" + events[j]]["handler"];
                    }
                }
            }
        }
    },
    /**
     * 取消所有绑定
     */
    unbindALL : function()
    {
        this.unbind(this.checkList);
    }
};